﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DDD.Core {

    using Vector3 = Vector3<float>;

    /// <summary>
    /// ライト・マテリアルクラス
    /// </summary>
    /// <remarks>
    /// ライトマテリアルは(その名の通り)ライトが持つべきマテリアルの一種です。
    /// パラメーターとしてライトカラー、強度、減衰定数、スポット角度などを持ちます。
    /// </remarks>
    public class LightMaterial : Material {

        #region Field
        Vector3 color;
        float intensity;
        float constantAttenuation;
        float linearAttenuation;
        float quadraticAttenuation;
        float spotAngle;            // in degree
        float spotExponential;
        #endregion

        #region Constructor
        /// <summary>
        /// コンストラクター
        /// </summary>
        /// <remarks>
        /// 初期値は､
        /// <list type="bullet">
        ///   <item><see cref="Color"/></item>=(1,1,1) 白
        ///   <item><see cref="Intensity"/>=1.0</item>
        ///   <item><see cref="Attenuation"/>=(1,0,0) 減衰なし</item>
        ///   <item><see cref="SpotAngle"/>=90度</item>
        ///   <item><see cref="SpotExponential"/>=10</item>
        /// </list>
        /// </remarks>
        public LightMaterial () {
            this.color = new Vector3 (1,1,1);
            this.intensity = 1.0f;
            this.constantAttenuation = 1;
            this.linearAttenuation = 0;
            this.quadraticAttenuation = 0;
            this.spotAngle = 90;
            this.spotExponential=10;
        }
        #endregion

        #region Property


        /// <summary>
        /// ライトの色
        /// </summary>
        /// <remarks>
        /// </remarks>
        public Vector3 Color {
            get { return color; }
        }
      
        /// <summary>
        /// ライトの強度
        /// </summary>
        public float Intensity {
            get { return intensity; }
        }

        /// <summary>
        /// ライトの減衰定数(Constant,Linear,Quadratic)の組
        /// </summary>
        public Vector3 Attenuation {
            get { return new Vector3 (constantAttenuation, linearAttenuation, quadraticAttenuation); }
        }

        /// <summary>
        /// ライトの減衰定数(Constant)
        /// </summary>
        public float ConstantAttenuation {
            get { return constantAttenuation; }
        }

        /// <summary>
        /// ライトの減衰定数(Linear)
        /// </summary>
        public float LinearAttenuation {
            get { return linearAttenuation; }
        }

        /// <summary>
        /// ライトの減衰定数(Quadratic)
        /// </summary>
        public float QuadraticAttenuation {
            get { return quadraticAttenuation; }
        }
        
        /// <summary>
        /// スポットライトの開き角度(in degree)
        /// </summary>
        /// <remarks>
        /// スポットライトの光の開き角度です。
        /// 中心から端までの角度ではなく、端から端までの角度です。
        /// </remarks>
        public float SpotAngle {
            get { return spotAngle; }
        }

        /// <summary>
        /// スポットライトの指数係数
        /// </summary>
        /// <remarks>
        /// 中心から周辺部に向かってどれぐらいの早さで暗くなるかを決める指数係数です。
        /// </remarks>
        public float SpotExponential {
            get { return spotExponential; }
        }

        /// <summary>
        /// このオブジェクトが参照する全DDDオブジェクトを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// 参照にnullは含まれません。同一のオブジェクトが複数出現する可能性があります。
        /// </remarks>
        public override IEnumerable<Object> References {
            get {
                return base.References;
            }
        }
        #endregion

        #region Method

        /// <summary>
        /// ライトの色を設定します
        /// </summary>
        /// <param name="r">赤[0,1]</param>
        /// <param name="g">緑[0,1]</param>
        /// <param name="b">青[0,1]</param>
        public void SetColor (float r, float g, float b) {
            if (r < 0 || r > 1) {
                throw new ArgumentException ("R is invalid");
            }
            if (g < 0 || g > 1) {
                throw new ArgumentException ("G is invalid");
            }
            if (b < 0 || b > 1) {
                throw new ArgumentException ("B is invalid");
            }
            this.color = new Vector3 (r, g, b);
        }

        /// <summary>
        /// ライトの強度を設定します
        /// </summary>
        /// <param name="intensity">強度</param>
        public void SetIntensity (float intensity) {
            if (intensity < 0) {
                throw new ArgumentException ("Intensity is invalid");
            }
            this.intensity = intensity;
        }

        /// <summary>
        /// ライトの減衰定数を設定します
        /// </summary>
        /// <param name="c">減衰定数(Constant)</param>
        /// <param name="l">減衰定数(Linear)</param>
        /// <param name="q">減衰定数(Quadratic)</param>
        public void SetAttenuation (float c, float l, float q) {
            if (c == 0 && l == 0 && q == 0) {
                throw new ArgumentException ("All 0 of attenuations is invalid");
            }
            if (c < 0 || l < 0 || q < 0) {
                throw new ArgumentException ("Attenuation factors are invalid");
            }
            this.constantAttenuation = c;
            this.linearAttenuation = l;
            this.quadraticAttenuation = q;
        }

        /// <summary>
        /// ライトのスポットパラメーターを設定します
        /// </summary>
        /// <param name="angle">角度(in degree)</param>
        /// <param name="exp">指数定数(exponential)</param>
        public void SetSpotParameter (float angle, float exp) {
            if (angle < 0 || angle > 180) {
                throw new ArgumentException ("Angle is invalid");
            }
            if (exp < 0 || exp > 1000) {
                throw new ArgumentException ("Exp is invalid");
            }

            this.spotAngle = angle;
            this.spotExponential = exp;
        }

        /// <summary>
        /// 現在のオブジェクトの状態を表す文字列を返します。
        /// </summary> 
        public override string ToString () {
            return string.Format("Light: Color={0}, Intensity={1}, Attenuation={2},{3},{4}", Color, Intensity, constantAttenuation, linearAttenuation, quadraticAttenuation);
        }
        #endregion
    }
}
